HJS

스프링 빈(Spring Bean)

Grid Template Areas 예시

1️⃣ 스프링 빈이란?

스프링 빈(Spring Bean)스프링 컨테이너가 생성하고 관리하는 객체를 말합니다.

보통 우리가 자바에서 객체를 사용할 땐 new 키워드로 직겁 생성합니다. 하지만 스프링에서는 객체 생성부터 의존성 주입까지 스프링이 대신 해주는 구조를 사용합니다.
이때 스프링이 관리하는 객체빈(Bean) 이라고 부릅니다.

예를 들어

public class HelloService {
	public String sayHello() {
		return "Hello, Spring!";
	}
}

이 클래스를 직접 사용하는 대신, 스프링에 등록하면 다음처럼 사용 가능

@Service
public class HelloService {
    public String sayHello() {
        return "Hello, Spring!";
    }
}

@RestController
public class HelloController {
    private final HelloService helloService;

    // 의존성 주입 (DI)
    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    @GetMapping("/hello")
    public String hello() {
        return helloService.sayHello();
    }
}

✔️ 스프링은 HelloService를 빈으로 등록하고, HelloController에 자동을 주입해줍니다.



2️⃣ 빈은 어떻게 등록할까?

스프링에서 빈을 등록하는 방법은 크게 2가지가 있습니다.

① 자동 등록 (컴포넌트 스캔 기반)

클래스에 어노테이션을 붙이면 스프링이 자동으로 빈으로 등록해줍니다.

어노테이션의미
@Component기본 컴포넌트
@Controller웹 요청 처리용
@Service비즈니스 로직용
@RepositoryDAO/DB 접근용
@Component
public class MyComponent {}

@Service
public class MyService {}

@Repository
public class MyRepository {}

@Controller
public class MyController {}

✔️ 이 방식은 @ComponentScan이 설정된 위치 이하의 클래스들만 스캔됩니다.

② 수동 등록 (Java Config 방식)

Java 설정 파일에서 직접 빈을 등록할 수 있습니다.

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }
}

✔️ 주로 외부 라이브러리처럼 내가 직접 어노테이션을 못 붙이는 클래스에 사용합니다.



3️⃣ 스프링 빈을 왜 쓰는가?

🔹 객체 관리 책임 분리 - 내가 new 하지 않아도 됨

💬 문제 상황

public class UserController {
    private UserService userService = new UserService(); // 직접 생성
}

⚠️ 클래스 내부에서 직접 객체를 만들면, 책임이 이곳저곳에 흩어 집니다.
⚠️ 누가 만들고 누가 관리하는지 파악하기 어렵습니다.

✅ 스프링 방식

@RestController
public class UserController {
    private final UserService userService;

    // 스프링이 UserService를 자동으로 주입!
    public UserController(UserService userService) {
        this.userService = userService;
    }
}


🔹 의존성 주입 (DI) – 바꿔 끼우기 쉬운 구조

💬 문제 상황

public class PayService {
    private final KakaoPay kakaoPay = new KakaoPay(); // 고정됨
}

⚠️ TossPay로 바꾸려면 PayService 코드를 수정해야 합니다.

✅ 스프링 방식

public interface Payment {
    void pay();
}

@Component
public class KakaoPay implements Payment {
    public void pay() { System.out.println("카카오 결제"); }
}

@Component
public class TossPay implements Payment {
    public void pay() { System.out.println("토스 결제"); }
}

@Service
public class PayService {
    private final Payment payment;

    public PayService(Payment payment) {
        this.payment = payment;
    }
}
🧐 @Primary, @Qualifier 예시
// @Primary 예시
@Component
@Primary
public class KakaoPay implements Payment {
    public void pay() { System.out.println("카카오 결제"); }
}
// PayService에서 기본적으로 KakaoPay가 주입
// @Qualifier 예시
@Service
public class PayService {
    private final Payment payment;

    public PayService(@Qualifier("tossPay") Payment payment) {
        this.payment = payment;
    }
}


🔹 싱글톤 관리 - 객체 하나로 다 같이 씀

💬 문제 상황

OrderService order1 = new OrderService();
OrderService order2 = new OrderService();

System.out.println(order1 == order2); // false

⚠️ 직접 생성하면 매번 새 객체 → 상태 불일치, 메모리 낭비 위험.

✅ 스프링 방식

@Component
public class OrderService {
    // 내부 로직
}
@Autowired
OrderService order1;

@Autowired
OrderService order2;

System.out.println(order1 == order2); // true